classdef SignalModulator < matlab.System
    % 信号调制器：实现GNSS（如GPS）信号的扩频码生成、符号调制、多天线旋转信号附加
    % 及中频上变频，支持多普勒频移、信道延迟、接收功率模拟和加性高斯白噪声（AWGN）添加
    % 参考"Glockenspiel"信号仿真框架，适配多天线接收场景

    properties(Nontunable)
        config              % 信号生成配置结构体（包含信号、天线、噪声、多普勒等参数）
        PRNID               % 伪码ID（用于生成对应卫星的扩频码，如GPS L1CA码的卫星ID）
        SampleRate          % 采样率(Hz)（信号采样的时间分辨率）
        CenterFrequency     % 中频载波频率(Hz)（调制后输出信号的中心频率，0表示基带）
        codeType            % 伪码类型（如'L1CA'表示GPS L1频段C/A码，'L5Q'表示L5频段Q码）
        codeRate            % 码速率(Hz)（扩频码的chip速率，如GPS L1CA码为1.023e6 Hz）
        codeLen             % 码长(chips)（单个扩频码周期的chip数，如GPS L1CA码为1023 chips）
        bitDuration         % 比特持续时间(s)（导航电文比特的时长，如GPS L1CA码为20ms/bit）
        numAntennas         % 天线数量（多天线接收场景下的天线总数）
        funcPwrRx           % 接收功率函数句柄（输入时间t，输出对应时刻的接收功率，模拟信号衰减）
        funcDoppler         % 多普勒相位函数句柄（输入时间t，输出对应时刻的多普勒相位，模拟卫星-接收机相对运动）
        funcDelay           % 信道延迟函数句柄（输入时间t，输出对应时刻的信道延迟，模拟信号传播时延）

        pMotionModel        % 运动模型对象（用于模拟卫星-接收机相对运动）
        pRotationModel      % 旋转模型对象（计算旋转对接收信号的影响）

        k = 1.38064852e-23; % 玻尔兹曼常数(J/K)（计算热噪声功率的物理常数）
        T = 300;             % 室温(K)（默认300K，用于计算热噪声功率）
        B = 7.2e6;           % 噪声带宽(Hz)（接收端滤波器的等效噪声带宽，如GPS L1信号典型值）

        seed = 'default';    % 随机数种子（用于初始化随机数流，保证仿真可复现）
        
        numSamplesPerInteg  % 每段积分时间的采样点数（根据跟踪器积分时间计算，用于分段输出信号）
    end

    properties(Access = private)
        codeBlocks          % 扩频码块（将单个码周期重复拼接，匹配比特持续时间的码序列）
        noisepower          % 噪声功率(W)（根据玻尔兹曼常数、温度和带宽计算的热噪声功率）
        randStream          % 随机数流（独立随机数生成器，用于导航电文比特和噪声的随机生成）
        bitLast             % 上一帧最后一个传输比特（0/1，用于保证比特连续性）
        iBitLast            % 上一帧最后一个比特的索引（用于跟踪比特序列的位置，保证连续性）
        pSampleIndexRx      % 下一帧接收信号的起始采样点索引（用于分段输出时的时间连续性）
        
        startTime           % 仿真开始的时间
        delay               % 信道延迟缓存（当前未启用，预留用于动态延迟更新）
    end

    methods(Access = public)
        function obj = SignalModulator(config, rxConfig)
            % 构造函数：初始化信号调制器的核心参数，从配置结构体中加载参数并绑定函数句柄
            % 输入：
            %   config - 信号生成配置结构体（包含signal/antennas/noise/doppler/delay/sim等字段）
            %   rxConfig - 接收端配置结构体（主要用于获取跟踪器积分时间，计算每段采样点数）

            % ---------------------- 1. 加载信号基础参数 ----------------------
            obj.config = config;
            obj.startTime = 0;
            obj.PRNID = config.signal.prnid;          % 伪码ID（如GPS卫星PRN号）
            obj.bitDuration = config.signal.bitDuration;  % 比特持续时间（如20ms）
            obj.codeType = config.signal.codeType;    % 伪码类型（如'L1CA'）
            obj.codeRate = config.signal.codeRate;    % 码速率（如1.023e6 Hz）
            obj.codeLen = config.signal.codeLen;      % 码长（如1023 chips）
            obj.SampleRate = config.signal.SampleRate;% 采样率（如4.092e6 Hz）
            obj.CenterFrequency = config.signal.CenterFrequency; % 中频频率
            obj.pMotionModel = MotionModel(config, rxConfig);  % 初始化运动模型对象
            obj.pRotationModel = RotationSignalModel(config, rxConfig);  % 初始化旋转模型对象

            % ---------------------- 2. 加载天线配置 ----------------------
            obj.numAntennas = config.antennas.count;  % 天线数量（多天线场景配置）


            % ---------------------- 4. 加载噪声相关参数 ----------------------
            obj.k = config.noise.boltzmann;   % 玻尔兹曼常数（从配置读取，支持自定义）
            obj.T = config.noise.temperature; % 温度（从配置读取，如300K）
            obj.B = config.noise.bandwidth;   % 噪声带宽（从配置读取，如7.2e6 Hz）
            obj.seed = config.sim.seed;       % 随机数种子（用于重置随机数流）

            % ---------------------- 5. 绑定信道函数句柄 ----------------------
            % 5.1 信道延迟函数：默认输出固定初始延迟（可扩展为动态延迟）
            obj.funcDelay = @(t) getDelay(obj.config, t);
            % 5.2 接收功率函数：默认输出固定接收功率（可扩展为随时间变化的功率）
            obj.funcPwrRx = @(t) getNoisePwrRx(obj.config, t);
            % 5.3 多普勒相位函数：根据配置选择"恒定多普勒"或"线性变化多普勒"
            obj.funcDoppler = @(t) getDoppler(obj.config, t);

            % ---------------------- 6. 计算每段积分时间的采样点数 ----------------------
            % 跟踪器总积分时间 × 采样率 = 每段输出信号的采样点数（分段处理信号）
            obj.numSamplesPerInteg = floor(config.signal.SampleRate * rxConfig.tracker.totalIntegTime); 
            
        end
    end

    methods(Access = protected)
        function setupImpl(obj)
            % 初始化函数：在流处理开始前执行，生成扩频码块、计算噪声功率并初始化状态变量
            % 作用：避免重复计算固定参数（如扩频码），保证流处理高效性

            % ---------------------- 1. 生成扩频码并进行BPSK调制 ----------------------
            % gnssCACode：生成GPS C/A码（1×1023逻辑值数组，0/1）
            % 1-2×double(...)：将0/1逻辑值转换为-1/1（BPSK调制，匹配扩频信号格式）
            cacode = 1 - 2 * double(gnssCACode(obj.PRNID, "GPS")); 
            % 计算单个比特持续时间内需要重复的码周期数：(比特时长×码速率)/码长
            numCodeRep = obj.bitDuration * obj.codeRate / obj.codeLen;
            % 拼接扩频码块：将单个码周期重复numCodeRep次，匹配1个比特的时长
            obj.codeBlocks = repmat(cacode, numCodeRep, 1);

            % ---------------------- 2. 计算热噪声功率 ----------------------
            % 公式：噪声功率 = 玻尔兹曼常数×温度×噪声带宽（N0*B = kTB）
            obj.noisepower = obj.k * obj.T * obj.B;

            % ---------------------- 3. 初始化随机数流和状态变量 ----------------------
            obj.randStream = RandStream('mt19937ar', 'Seed', obj.seed); % 重置随机数流
            obj.bitLast = 0;              % 初始上一比特值：默认0（可随机）
            obj.iBitLast = 0;             % 初始上一比特索引：从0开始
            obj.pSampleIndexRx = obj.SampleRate * obj.startTime;       % 初始接收采样点索引：从startTime开始
        end

        function [waveformRx, tRx] = stepImpl(obj)
            % 流处理单步函数：每调用一次输出一段积分时间的调制信号（含噪声）
            % 输出：
            %   waveformRx - 输出的接收信号（含噪声，长度=numSamplesPerInteg）
            %   tRx - 当前段信号的起始时间（s），用于时间同步

            % ---------------------- 1. 计算当前段信号的时间信息 ----------------------
            tRx = obj.pSampleIndexRx / obj.SampleRate; % 当前段起始时间：采样索引/采样率
            % 生成当前段的所有采样点索引（从pSampleIndexRx开始，共numSamplesPerInteg个）
            sampleIndicesRx = obj.pSampleIndexRx + (0 : obj.numSamplesPerInteg - 1)';
            % 计算每个采样点的接收时间（s）：采样索引/采样率
            sampleTimesRx = sampleIndicesRx / obj.SampleRate;

            % ---------------------- 2. 计算发射端时间和索引（考虑信道延迟） ----------------------
            % 发射时间 = 接收时间 - 信道延迟（将接收时间映射到发射时刻，用于获取对应码/比特）
            % 注：延迟单位为码片(chip)，需除以码速率转换为时间(s)
            sampleTimesTx = sampleTimesRx - obj.funcDelay(sampleTimesRx) / obj.codeRate;
            % 发射端比特索引：接收时间/比特时长，向下取整（确定每个采样点对应哪个比特）
            bitIndicesTx = floor(sampleTimesRx / obj.bitDuration);
            % 发射端码索引：发射时间×码速率，对码块长度取模（确定每个采样点对应哪个chip）
            codeIndicesTx = mod(floor(sampleTimesTx * obj.codeRate), length(obj.codeBlocks));
            % 计算当前段覆盖的比特数：最后一个比特索引 - 第一个比特索引 + 1
            numBits = bitIndicesTx(end) - bitIndicesTx(1) + 1;

            % ---------------------- 3. 生成导航电文比特（保证连续性） ----------------------
            if bitIndicesTx(1) == obj.iBitLast 
                % 情况1：当前段第一个比特与上一段最后一个比特连续
                % 拼接比特序列：上一段最后一个比特 + 新生成的(numBits-1)个随机比特（0/1）
                bits = [obj.bitLast; randi(obj.randStream, [0 1], numBits - 1, 1)];
                % 比特索引序列：从上一段最后一个索引开始连续编号
                iBits = obj.iBitLast + (0 : numBits - 1);
            else
                % 情况2：当前段与上一段比特不连续（如首次启动或重置后）
                % 生成numBits个全新的随机比特（0/1）
                bits = randi(obj.randStream, [0 1], numBits, 1);
                % 比特索引序列：从上一段最后一个索引的下一个开始编号
                iBits = obj.iBitLast + (1 : numBits);
            end

            % ---------------------- 4. 生成基带扩频信号 ----------------------
            % 从码块中提取对应索引的chip值，乘以比特值（1/0→1/-1，BPSK调制）
            % waveformBaseband = obj.codeBlocks(codeIndicesTx + 1) .* 1; 
            waveformBaseband = obj.codeBlocks(codeIndicesTx + 1) .* (1 - 2 * bits(bitIndicesTx - bitIndicesTx(1) + 1));

            % ---------------------- 5. 中频上变频与多普勒相位附加 ----------------------
            % 计算中频载波相位：2π×中频频率×接收时间 + 多普勒相位 + 初始相位（0）
            % 作用：将基带信号调制到中频，同时附加多普勒频移（模拟卫星运动）

            % 旋转对接收信号载波功率的影响和相位影响
            % 作用：模拟接收天线旋转，改变接收信号的载波功率和相位
            % motionData = obj.pMotionModel.step(sampleTimesRx);
            % rotationData = obj.pRotationModel.step(motionData);
            rotationData = getMockRotData(sampleTimesRx, obj.config.rotation.freq, obj.numAntennas);

            IFCarrier = exp(1j * (2 * pi * obj.CenterFrequency * sampleTimesRx + obj.funcDoppler(sampleTimesRx) + rotationData.phaseRot.'));
            % 中频信号 = 中频载波 × 基带扩频信号（复数调制，单位功率）
            waveformIF = IFCarrier .* waveformBaseband; 
            % 若中频频率非0，取实部（实际硬件输出实信号，功率减半）；基带（中频=0）保留复数
            if obj.CenterFrequency ~= 0
                waveformIF = real(waveformIF);
            end   

            % ---------------------- 6. 信号功率缩放与加性噪声添加 ----------------------
            % 6.1 计算信号功率和信噪比：接收功率从函数句柄获取（db2pow转换为线性功率）
            signalpower = db2pow(obj.funcPwrRx(sampleTimesTx));
            signalplusnoisepower = signalpower + obj.noisepower; % 信号+噪声总功率
            % 6.2 信号功率缩放：乘以信号功率的平方根（匹配设定的接收功率）
            waveformScaled = sqrt(signalpower) .* waveformIF; 
            % 6.3 生成加性高斯白噪声（AWGN）：复数噪声，功率=设定的噪声功率
            noise = wgn(obj.numSamplesPerInteg, 1, db(obj.noisepower, 'power'), [], obj.randStream, 'complex');
            % 若中频非0，噪声取实部（与信号格式一致）
            if obj.CenterFrequency ~= 0
                noise = real(noise);
            end
            % 6.4 合成接收信号：缩放后的信号 + 噪声，除以总功率平方根（可选归一化）
            waveformRx = (waveformScaled .* sqrt(rotationData.powerRot).' + noise) ./ sqrt(signalplusnoisepower);

            % ---------------------- 7. 更新状态变量（保证下一段连续性） ----------------------
            obj.pSampleIndexRx = obj.pSampleIndexRx + obj.numSamplesPerInteg; % 更新采样点索引
            obj.bitLast = bits(end); % 记录当前段最后一个比特值
            obj.iBitLast = iBits(end); % 记录当前段最后一个比特索引
        end

        function resetImpl(obj)
            % 重置函数：当流处理重置时（如重新开始仿真），恢复初始状态
            obj.randStream = RandStream('mt19937ar', 'Seed', obj.seed); % 重置随机数流
            obj.pSampleIndexRx = obj.SampleRate * obj.startTime;       % 采样点索引重置
            obj.bitLast = 0;              % 上一比特值重置为0
            obj.iBitLast = -1;            % 上一比特索引重置为-1（表示无历史比特）
        end
    end

end
